package avicit.bdp.dcs.precheck.service;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.utils.CheckParaUtils;
import avicit.bdp.common.utils.enums.JobType;
import avicit.bdp.dcs.datasource.service.BaseDbService;
import avicit.bdp.dcs.datasource.service.TableService;
import avicit.bdp.dcs.job.dto.JobDTO;
import avicit.bdp.dcs.job.service.DcsJobService;
import avicit.bdp.dcs.precheck.dao.PrecheckItemDao;
import avicit.bdp.dcs.precheck.dto.*;
import avicit.bdp.dcs.tools.enums.CheckItem;
import avicit.bdp.dcs.tools.enums.PrecheckResult;
import avicit.bdp.dcs.tools.enums.ProcessMode;
import avicit.bdp.dcs.tools.enums.TransferStatus;
import avicit.bdp.dcs.utils.DcsErrorCode;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.exception.BusinessException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @金航数码科技有限责任公司
 * @作者：liyb
 * @邮箱：liyb@avic-digital.com
 * @创建时间：2020-08-05 14:37
 * @类说明：
 * @修改记录：
 */
@Service
@Transactional
public class PrecheckService extends BaseService<PrecheckItemDao, PrecheckItemDTO> {

    private static final Logger logger = LoggerFactory.getLogger(PrecheckService.class);

    @Autowired
    private JobPrecheckResultService jobPrecheckResultService;
    @Autowired
    private JobTypePrecheckService jobTypePrecheckService;
    @Autowired
    private JobPrecheckItemService jobPrecheckItemService;
    @Autowired
    private DcsJobService jobService;
    @Autowired
    private TableService tableService;
    @Autowired
    private BaseDbService baseDbService;

    /**
     * 保存预检查信息
     * @param jobId
     * @return
     * @throws Exception
     */
    public String saveJobPrecheck(String jobId) throws Exception {
        logger.info("保存预检查,jobId:{}", jobId);
        JobDTO jobDTO = jobService.selectByPrimaryKey(jobId);
        if (jobDTO == null) {
            throw new BusinessException(String.format(DcsErrorCode.JOB_NULL.getDesc(), jobId));
        }
        // 先删除再新增
        jobPrecheckResultService.deleteByJobId(jobId);

        List<JobtypePrecheckDTO> jobtypePrecheckList = jobTypePrecheckService.getJobtypePrecheckList(jobDTO.getJobType());
        if (CollectionUtils.isNotEmpty(jobtypePrecheckList)) {
            for (JobtypePrecheckDTO jobtypePrecheck : jobtypePrecheckList) {

                // 如果处理模式是覆盖目标表或保留目标表或清空目标表数据，则预检查中没有“同名表存在性检查”
                PrecheckItemDTO precheckItemDTO = null;
                if (jobDTO.getProcessMode() == ProcessMode.COVER_DST_TABLE.getCode() ||
                        jobDTO.getProcessMode() == ProcessMode.RETAIN_DST_TABLE.getCode() ||
                        jobDTO.getProcessMode() == ProcessMode.CLEAR_DST_TABLE_DATA.getCode()) {
                    precheckItemDTO = jobPrecheckItemService.selectByPrimaryKey(jobtypePrecheck.getPrecheckItemId());
                }
                JobPrecheckResultDTO jobPrecheckResultDTO = new JobPrecheckResultDTO();
                jobPrecheckResultDTO.setId(ComUtil.getId());
                jobPrecheckResultDTO.setJobId(jobId);
                jobPrecheckResultDTO.setPrecheckItemId(jobtypePrecheck.getPrecheckItemId());
                jobPrecheckResultDTO.setResult(PrecheckResult.WAIT.getCode());

                // 检测项编码，源库连接性检查：1，目的库连接性检查：2，源库权限检查：3：，目的库权限检查：4，同名表存在性检查：5
                if (precheckItemDTO != null && precheckItemDTO.getCheckItemCode() == CheckItem.SAME_TABLE_CHECK.getCode()) {

                } else {
                    jobPrecheckResultService.insert(jobPrecheckResultDTO);
                    BdpLogUtil.log4Insert(jobPrecheckResultDTO);
                }
            }
        }
        return null;
    }

    /**
     * 更新预检查信息
     * @param jobId
     * @return
     * @throws Exception
     */
    public String updateJobPrecheck(String jobId) throws Exception {
        logger.info("更新预检查为等待状态,jobId:{}", jobId);
        JobPrecheckResultDTO jobPrecheckResultDTOQuery = new JobPrecheckResultDTO();
        jobPrecheckResultDTOQuery.setJobId(jobId);
        List<JobPrecheckResultDTO> jobPrecheckResultList = jobPrecheckResultService.selectList(jobPrecheckResultDTOQuery);
        if (CollectionUtils.isNotEmpty(jobPrecheckResultList)) {
            for (JobPrecheckResultDTO precheckResult : jobPrecheckResultList) {
                precheckResult.setResult(PrecheckResult.WAIT.getCode());
                jobPrecheckResultService.updateByPrimaryKeySelective(precheckResult);
            }
        }
        return null;
    }

    /**
     * 获取预检查进度
     * @param jobId
     * @return
     * @throws Exception
     */
    public PrecheckProgressDto getPrecheckListAndProgress(String jobId) throws Exception {
        logger.info("查询预检查进度,jobId:{}", jobId);
        JobDTO jobDTO = jobService.selectByPrimaryKey(jobId);
        CheckParaUtils.checkDbObjectValidity(jobDTO, logger, String.format(DcsErrorCode.JOB_NULL.getDesc(), jobId));

        PrecheckProgressDto precheckProgressDto = new PrecheckProgressDto();
        List<DcsJobPrecheckDto> precheckDtoList = jobPrecheckResultService.getPrecheckListByJobId(jobId);
        int successNum = 0;
        int completeNum = 0;
        if (CollectionUtils.isNotEmpty(precheckDtoList)) {
            for (DcsJobPrecheckDto jobPrecheckDto : precheckDtoList) {
                if (jobPrecheckDto.getResult() == PrecheckResult.SUCCESS.getCode()) {
                    successNum += 1;
                    completeNum += 1;
                }
                if (jobPrecheckDto.getResult() == PrecheckResult.FAIL.getCode()) {
                    completeNum += 1;
                }
            }
        }

        float progress = successNum * 1f / precheckDtoList.size();
        precheckProgressDto.setPrecheckDtoList(precheckDtoList);
        precheckProgressDto.setProgress(Math.round(progress * 100) + "%");
        precheckProgressDto.setComplete(completeNum == precheckDtoList.size() ? true : false);
        return precheckProgressDto;
    }

    public void executePrecheck(String jobId) throws Exception {
        // 将具体执行预检查放入线程池执行
        execute(jobId);
    }

    /**
     * 执行预检查
     *
     * @param jobId
     * @return
     * @throws Exception
     */
    public void execute(String jobId) throws Exception {
        logger.info("**********************开始执行预检查,jobId={}", jobId);
        // 作业总状态
        boolean jobStatus = true;

        // 1、更新任务状态 = 预检中
        JobDTO jobDTO = jobService.selectByPrimaryKey(jobId);
        CheckParaUtils.checkDbObjectValidity(jobDTO, logger, String.format(DcsErrorCode.JOB_NULL.getDesc(), jobId));

        jobDTO.setJobStatus(TransferStatus.PRECHECKING.getCode());
        jobService.updateByPrimaryKeySelective(jobDTO);

        // 2、查询待检查预检查项目
        List<DcsJobPrecheckDto> precheckDtoList = jobPrecheckResultService.getPrecheckListByJobId(jobId);

        // 3、根据检测项编码进行各种类型的预检查
        // 检测项编码，源库连接性检查：1，目的库连接性检查：2，源库权限检查：3：，目的库权限检查：4，同名表存在性检查：5，表名称合法性校验：6
        if (CollectionUtils.isNotEmpty(precheckDtoList)) {
            for (DcsJobPrecheckDto jobPrecheckDto : precheckDtoList) {
                JobPrecheckResultDTO jobPrecheckDTO = jobPrecheckResultService.selectByPrimaryKey(jobPrecheckDto.getId());
                jobPrecheckDTO.setResult(PrecheckResult.EXECUTING.getCode());
                jobPrecheckResultService.updateByPrimaryKeySelective(jobPrecheckDTO);

                CheckItem checkItem = CheckItem.valueOf(jobPrecheckDto.getCheckItemCode());
                switch (checkItem) {
                    // 源库连接性检查：1
                    case SRC_DB_CONNECT_CHECK:
                        if (jobDTO.getJobType() == JobType.SEMISTRUCTURE.getCode()) {
                            String systemBuilt = jobDTO.getExtraParams();
                            // 非系统内置数据源才做检查
                            if (StringUtils.isEmpty(systemBuilt) || "0".equals(systemBuilt)) {
                                if (baseDbService.dataSourceConnTest(jobDTO.getSrcDatasourceId())) {
                                    jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                                    logger.info("源库连接性检查成功,jobId:{}", jobId);
                                } else {
                                    jobPrecheckDTO.setResult(PrecheckResult.FAIL.getCode());
                                    jobStatus = false;
                                    logger.info("源库连接性检查失败,jobId:{}", jobId);
                                }
                            }else{
                                jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                            }
                        }else{
                            if (baseDbService.dataSourceConnTest(jobDTO.getSrcDatasourceId())) {
                                jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                                logger.info("源库连接性检查成功,jobId:{}", jobId);
                            } else {
                                jobPrecheckDTO.setResult(PrecheckResult.FAIL.getCode());
                                jobStatus = false;
                                logger.info("源库连接性检查失败,jobId:{}", jobId);
                            }
                        }
                        break;

                    // 目的库连接性检查：2
                    case DST_DB_CONNECT_CHECK:
                        if (baseDbService.dataSourceConnTest(jobDTO.getDstDatasourceId())) {
                            jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                            logger.info("目的库连接性检查成功,jobId:{}", jobId);
                        } else {
                            jobPrecheckDTO.setResult(PrecheckResult.FAIL.getCode());
                            jobStatus = false;
                            logger.info("目的库连接性检查失败,jobId:{}", jobId);
                        }
                        break;

                    // 源库权限检查：3
                    case SRC_DB_AUTH_CHECK:
                        jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                        break;

                    // 目的库权限检查：4
                    case DST_DB_AUTH_CHECK:
                        jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                        break;

                    // 同名表存在性检查：5
                    case SAME_TABLE_CHECK:
                        // 有个特殊的例外，数据同步配置为(不开启表结构初始化，预检查并报错拦截)，此时若目标表不存在则检查失败
                        if (!tableService.sameTableNameTest(jobId)) {
                            jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                            logger.info("同名表存在性检查成功,jobId:{}", jobId);
                        } else {
                            jobPrecheckDTO.setResult(PrecheckResult.FAIL.getCode());
                            jobStatus = false;
                            logger.info("同名表存在性检查失败,jobId:{}", jobId);
                        }
                        break;

                    // 表名称合法性校验：6
                    case TABLE_NAME_CHECK:
                        if (tableService.isValidTableName(jobId)) {
                            jobPrecheckDTO.setResult(PrecheckResult.SUCCESS.getCode());
                            logger.info("表名称合法性校验成功,jobId:{}", jobId);
                        } else {
                            jobPrecheckDTO.setResult(PrecheckResult.FAIL.getCode());
                            jobStatus = false;
                            logger.info("表名称合法性校验失败,jobId:{}", jobId);
                        }
                        break;
                    default:
                        logger.warn("不支持的预检查类型:{}", checkItem);
                        break;
                }
                jobPrecheckResultService.updateByPrimaryKeySelective(jobPrecheckDTO);
            }
        }

        // 4、回写检查结果，检查成功、检查失败
        if (jobStatus) {
            jobDTO.setJobStatus(TransferStatus.NOT_STARTED.getCode());
        } else {
            jobDTO.setJobStatus(TransferStatus.PRECHECK_FAIL.getCode());
        }

        jobService.updateByPrimaryKeySelective(jobDTO);
        logger.info("**********************完成执行预检查,jobId={},检查结果={}", jobId, jobStatus);
    }

}
